/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.loja.view.swing;

import br.com.loja.model.service.exception.ServiceException;
import br.com.loja.model.service.factory.ServiceFactory;
import br.com.loja.util.reflection.JBean;
import br.com.loja.util.reflection.ReflectionUtil;
import br.com.loja.view.component.factory.ComponentFactory;
import br.com.loja.view.component.factory.ComponentProducer;
import br.com.loja.view.component.factory.ViewHadler;
import br.com.loja.view.swing.model.FormField;
import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.FormLayout;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Map;
import javax.persistence.GeneratedValue;
import javax.persistence.Transient;
import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;

/**
 *
 * @author DavidLuiz
 */
public class PanelEntityEditor<T> extends JPanel implements IFormEntity<T> {

    private final Class<T> entityClass;
    private JPanel cardPanel;
    private final JComboBox comboBoxPanel = new JComboBox();
    private Map<String, Component> componentsAttributes;

    public PanelEntityEditor(Class<T> entityClass) throws ServiceException {
        this.entityClass = entityClass;
        initComponents();
    }

    private void initComponents() throws ServiceException {

        comboBoxPanel.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                ((CardLayout) cardPanel.getLayout()).show(cardPanel,
                        (String) e.getItem());
            }
        });

        setLayout(new GridLayout(1, 2));
        add(getPanelEntity());
        //
        if (cardPanel != null) {
            add(buildPanelCard());
        }

        disableForm(false);
    }

    private JPanel getPanelEntity() throws ServiceException {

        DefaultFormBuilder builder = new DefaultFormBuilder(new FormLayout(""));
        builder.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        builder.appendColumn("right:pref");
        builder.appendColumn("3dlu");
        builder.appendColumn("fill:max(pref; 180px)");

        builder.appendSeparator(entityClass.getSimpleName());

        for (Field field : ReflectionUtil.getAllFields(entityClass)) {

            if (!isMappable(field)) {
                continue;
            }

            ComponentFactory factory = new ComponentProducer(field).getComponentFactory();
            Component component = factory.getComponent();

            if (component != null) {

                component.setName(field.getName());

                component = buildScrollPane(component);

                builder.nextLine();
                builder.append(component.getName(), component);

                if (Collection.class.isAssignableFrom(field.getType())) {
                    addCardPanel(getPanelEditor(field), field.getName());
                }
            }
        }

        JPanel panel = builder.getPanel();
        panel.setName(entityClass.getSimpleName());
        componentsAttributes = ComponentUtil.getComponents(panel);

        return panel;

    }

    private Component buildScrollPane(Component component) {
        if (component instanceof JTable) {
            component = getScrollPane((JTable) component);
        }
        if (component instanceof JList) {
            component = getScrollPane((JList) component);
        }
        return component;
    }

    private JScrollPane getScrollPane(Component component, Dimension dimension) {
        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setName("List " + component.getName());
        scrollPane.setPreferredSize(dimension);
        scrollPane.getViewport().add(component);
        return scrollPane;
    }

    private JScrollPane getScrollPane(JTable component) {
        JScrollPane scrollPane = getScrollPane(component, new Dimension(180, 240));
        return scrollPane;
    }

    private JScrollPane getScrollPane(JList component) {
        Dimension dimension = new Dimension(180, 100);
        JScrollPane scrollPane = getScrollPane(component, dimension);
        return scrollPane;
    }

    private JPanel getPanelEditor(Field field) throws ServiceException {
        ComponentToManyFactoryOld factory = new ComponentToManyFactoryOld(field, ServiceFactory.getServiceFactory());
        return (JPanel) factory.getComponent();
    }

    private boolean isMappable(Field field) {
        if (haveNoValidModifier(field)) {
            return false;
        }

        return !haveNoValidAnnotation(field);
    }

    private boolean haveNoValidAnnotation(Field field) {
        for (Annotation annotation : field.getAnnotations()) {
            if (isNoValid(annotation)) {
                return true;
            }
        }

        return false;
    }

    private boolean haveNoValidModifier(Field field) {
        return isStatic(field) || isTransient(field);
    }

    private boolean isNoValid(Annotation annotation) {
        return !isVisibleFormField(annotation)
                || isGeneratedValue(annotation);
    }

    private boolean isGeneratedValue(Annotation annotation) {
        return annotation instanceof GeneratedValue;
    }

    private boolean isVisibleFormField(Annotation annotation) {
        if (annotation instanceof FormField) {
            FormField formField = (FormField) annotation;
            return formField.visible();
        }

        return true;
    }

    private boolean isTransient(Field field) {
        return Modifier.isTransient(field.getModifiers());
    }

    private boolean isStatic(Field field) {
        return Modifier.isStatic(field.getModifiers());
    }

    private void addCardPanel(JPanel panel, String panelName) {
        if (panel == null) {
            return;
        }

        getCardPanel().add(panel, panelName);
        comboBoxPanel.addItem(panelName);

    }

    private JPanel getCardPanel() {
        if (cardPanel == null) {
            cardPanel = new JPanel(new CardLayout());
        }
        return cardPanel;
    }

    private JPanel buildPanelCard() {
        if (cardPanel.getComponents().length == 1) {
            return cardPanel;
        }

        JPanel panel = new JPanel(new BorderLayout(5, 5));

        JPanel panelSelctionCard = new JPanel(new FlowLayout(FlowLayout.LEADING));
        panelSelctionCard.add(new JLabel("Attributes"));
        panelSelctionCard.add(comboBoxPanel);

        panel.add(panelSelctionCard, BorderLayout.NORTH);
        panel.add(cardPanel, BorderLayout.CENTER);

        return panel;
    }

    @Override
    public void clearForm() {
        setModel(null);
    }

    @Override
    public void disableForm(boolean op) {
        for (Map.Entry<String, Component> entrySet : componentsAttributes.entrySet()) {
            Component component = entrySet.getValue();
            component.setEnabled(op);
        }
    }

    @Override
    public void setModel(T model) {

        for (Field field : ReflectionUtil.getAllFields(entityClass)) {

            Component component = componentsAttributes.get(field.getName());

            if (component == null) {
                continue;
            }

            ViewHadler<T> viewHadler = (ViewHadler<T>) component;

            if (model == null) {
                viewHadler.setValueModel(null);
                continue;
            }

            JBean bean = new JBean(model, field.getName());
            Object value = bean.getValue();

            viewHadler.setValueModel((T) value);

        }

    }

    @Override
    public T getModel(T model) {

        for (Field field : ReflectionUtil.getAllFields(entityClass)) {

            Component component = componentsAttributes.get(field.getName());

            if (component == null) {
                continue;
            }

            ViewHadler viewHadler = (ViewHadler) component;

            Object value = viewHadler.getValueModel();

            JBean bean = new JBean(model, field.getName());
            bean.setValue(value);
        }

        return model;

    }

    @Override
    public void grabFocus() {
        for (Map.Entry<String, Component> entrySet : componentsAttributes.entrySet()) {
            Component component = entrySet.getValue();
            if (component.isFocusable()) {
                ((JComponent) component).grabFocus();
                break;
            }

        }
    }

}
